UZI is coded in decent, recent
and standard C++.
Because UZI is supposed to run on any platform, a drastic selection has
been made on the additional libraries/dependencies required.
Similarly, UZI code base is kept simple to avoid current
-memory-hungry- application behaviour.
From my previous experiencies, there is no truely cross-platform STL
library. Similarly, Boost library was forbidden for it's bad behaviour
on compilation time, and STL library dependencies.
A. Decent C++
This means that obfuscated C++ (like obscure operator overloading,
mixed private & public inheritance, ...) are not/must not be
used.
In case operator overloading is not obvious, but required for code
simplification, it's clearly stated in the documentation.
At the same time, almost all C++ specific features are used (like
placement new, pure virtual methods, constructor based initialization,
namespaces, template etc...), so it's required to know this C++ rules
in order
to be able to understand the current code, and write new code.
Almost all modern
object-oriented programming langage use these features, so this
shouldn't be to different to Java, D and C# developers.
Exceptions should be avoided as much as possible as
exception handling takes time, and isn't implemented correctly on every
platform.
The code follows strong coding conventions which are:
- Every namespace, typedef, define, class, member, method is
documented following DOxygen syntax (see below for code example).
This allows documentation to always be in-sync with code.
- Documentation is grouped by semantically-equivalent types
(members are grouped by accessibility, method are grouped by interface,
type definition are grouped too, etc...). This allows fast code review
- Method and functions must document their parameters names,
return types, and usage description.
- Code should follow basic Object-oriented programming
concept (such as encapsulation, interfaces)
- Member names are capitalized, except for the first word
(see below for code example). There is no prefix letter, nor suffix.
- Member names are expected to be short, but still made of
entire
words (
theNumberOfRows
is not good, while rowCount
is)
- Methods follow member rules, expect for one-word method
name
which are capitalized (like
setAnswerCode()
and Suicide()
)
- Methods that shouldn't modify their object must be declared
const
- Similarly, when an object is shared in multithreaded
environment, it should be declared volatile, method that accept being
called by multiple thread simultaneously must be declared volatile.
- Declaration should be in header files with
.hpp
extension,
and definition should be in source file with
.cpp
extension.
- Files should be grouped semantically in the same folder
with the common semantic name (like GUI, Component, Parser, Strings,
and so on)
- Method definition shouldn't be too long. If a method
definition is too long, it should be split in multiple sub
method.
- When dealing with an external object that won't change in
the whole scope, a reference is preffered instead of pointers. There is
less code to type, and it's easier to read.
- When returning the address of an object, a pointer
is used only
if it's required to return 0 as an error code.
- Preprocessor macro use should be avoided as much as possible
- Following those rules make the code easy to read and
understand, and ensure developers won't waste their time rewriting an
existing code.
B. Recent C++
The C++ features used include templates, placement new and exceptions.
This appeared in ANSI standardization of 1998.
UZI uses these features, hence can't be compiled on previous compiler.
However, some new features are not used (like RTTI, partial template
specialization,
any
type, and others).
Even if the next definition is not acceptable, let's say
that Visual C++ 6.0 compiler should be able to compile UZI in
any current or future version.
This allows Embedded Visual C++ 4.0 to be used too.
C. Standard C++
All platform specific code is moved to very specific files, so when
compiling on a different platform, the minimum porting work is required
on those files only.
This means that the code shouldn't be encumbered by #ifdef _WIN32 and
other oddities.
Because of poor Microsoft based compiler, some warning have to be
turned off with #pragma specific directive, and they should be done in
a common file.
Please avoid compiler specific extension (like variable parameters
macro of gcc) as it breaks on standard C++ compiler.
D. Documentation
UZI is using
Doxygen as the documentation engine.
This means that you can use any
Doxygen command inside your comment to see them appearing in the documentation.
By convention, we only use
/**
Doxygen's start code for general comments and
//!<
for inline enum member documentation, and
Doxygen's command starts by @.
Everything must be documented (from private members to public static functions, if any).
Functions and methods must have both their parameters and return type documented (unless it's obvious).
If a function or a class is usage is non-trivial, it's recommended to document its usage in @code / @endcode blocs with
//
comments.
E. Members grouping
Even with the best documentation around, there are still reasons to dig in the code.
In order to make this experience the less painful as possible, we usually group functionally equivalent member in the same section, with a
// comment
before the section access policy (public, private or protected).
It's common to find a
// Member
section, a
// ABaseClass Interface
section, a
// Helpers
section, a
// Construction and destruction
section and so on, in the current classes.
Example source code and its documentation, and some
comment
Header : Elements.hpp in
include/HTMLParser folder |
Comment |
/**
The base element class
The positions are set like specified in this schematic :
@verbatim
<element some_attribute>
|
|__ endPosition
|__ startPosition
... [ Content ] ...
</element
>
|
|___ finalPosition
|__ endTagPosition
@endverbatim
If the element doesn't have an end tag, either it is because:
- No end tag is allowed (like BR), then both its endTagPosition and
finalPosition are set to endPosition
- The end tag is optional (like LI), then both its endTagPosition and
finalPosition are set to the found end position
(either on startPosition of a new element of the same type) or parent's
endTagPosition
*/
class
Element
{
// Type definitions
public:
/** Inject the Element ID enumeration in our class */
typedef
HTML::GenericElement::ElementID
ElementID;
/** The validation rules type
definition */
typedef
const HTML::GenericElement
* const
ValidationRules;
// Members
private:
/**
The element type */
const ElementID
elementType;
/**
The element start position in stream */
uint32
startPosition;
/**
The element end position in stream */
uint32
endPosition;
//
Interface
public:
/**
This method returns the tag name as a char array */
const
tchar
* tagName() const
volatile { return
elementType
== HTML::GenericElement::Unknown ? "" : allowedElements[(int)elementType -
1].name;
}
/**
Get the rule for this element or 0 if not identifiable element */
inline
ValidationRules
getValidationRules() const
{ return
elementType
== HTML::GenericElement::Unknown ? 0 :
&allowedElements[(int)elementType
- 1]; }
// Accessors
public:
/**
Set the end position
@param position The new end position
@sa Element for details where the position are */
inline
void
setEndPosition(uint32
position) { endPosition =
position; }
/**
Set the start position
@param position The new start position
@sa Element for details where the position are */
inline
void
setStartPosition(uint32
position) { startPosition =
position; }
/**
Get the start position */
inline
uint32
getStartPosition() const { return startPosition; }
/**
Get the end position */
inline
uint32
getEndPosition() const { return endPosition; }
/**
Get the element type */
inline
const ElementID
& getElementType() const
{ return
elementType;
}
//
Construction
public:
/**
Default constructor */
Element(const ElementID type =
HTML::GenericElement::Unknown, uint32
startPos = 0, uint32
endPos = 0) : elementType(type),
startPosition(startPos),
endPosition(endPos)
{}
/**
Copy constructor */
Element(const Element
& element) : elementType(element.elementType),
startPosition(element.startPosition),
endPosition(element.endPosition)
{}
};
|
All documentation
start by /** and
finish by */
For enumeration, it's required to document every member with //!<
Documentation follow Doxygen format
Members are grouped by semantical functions
Every member is documented
Members themselves are private
If they don't change, member are declared const
Every member word is capitalized, expect the first
Method are also grouped by semantical functions
When a method can be called by multiple thread, it must be volatile
Similarly when a method doesn't modify the object, it must be const
Methods that use a parameter must document it
Return should be documented, unless obvious
Construction and destruction should be in their own section too.
|